package ru.usu.gv.utils.generators;

import ru.usu.gv.graph.Graph;
import ru.usu.gv.graph.Vertex;
import ru.usu.gv.utils.RandomUtilities;

import java.util.ArrayList;
import java.util.List;

/**
 * @author spupyrev
 * 17.11.2008
 */
public class ClusterGenerator
{
	public interface VertexCreator
	{
		Vertex createVertex();
	}

	public static Graph generateGraph()
	{
		return generateGraph(new VertexCreator()
		{
			public Vertex createVertex()
			{
				return new Vertex();
			}
		});
	}

	public static Graph generateGraph(VertexCreator creator)
	{
		Graph graph = new Graph();

		List<Integer> clique1 = new ArrayList();
		clique1.add(20);
		clique1.add(20);
		clique1.add(20);
		clique1.add(20);
		clique1.add(20);

		List<Integer> ind1 = addEdges(creator, graph, clique1, 0.50);

		List<Integer> clique2 = new ArrayList();
		clique2.add(15);
		clique2.add(15);
		clique2.add(15);

		List<Integer> ind2 = addEdges(creator, graph, clique2, 0.5);

		addEdges(graph, ind1, ind2, 0.05);

		return graph;
	}

	public static Graph generateGraph2(VertexCreator creator)
	{
		int tmp = 10;

		Graph graph = new Graph();

		List<Integer> clique1 = new ArrayList();
		clique1.add(20 * tmp);
		clique1.add(20 * tmp);
		clique1.add(20 * tmp);
		clique1.add(20 * tmp);
		List<Integer> ind1 = addEdges(creator, graph, clique1, 0.50);

		List<Integer> clique2 = new ArrayList();
		clique2.add(15 * tmp);
		clique2.add(15 * tmp);
		clique2.add(15 * tmp);
		List<Integer> ind2 = addEdges(creator, graph, clique2, 0.2);

		List<Integer> clique3 = new ArrayList();
		clique3.add(70);
		clique3.add(50);
		clique3.add(70);
		List<Integer> ind3 = addEdges(creator, graph, clique3, 0.2);

		addEdges(graph, ind1, ind2, 0.05);
		addEdges(graph, ind1, ind3, 0.1);
		addEdges(graph, ind2, ind3, 0.07);

		return graph;
	}

	private static List<Integer> addEdges(VertexCreator creator, Graph graph, List<Integer> clique, double d)
	{
		List<Integer> result = new ArrayList<Integer>();

		int sum[] = new int[clique.size()];
		//adding inner edges
		for (int i = 0; i < clique.size(); i++)
		{
			sum[i] = (i == 0 ? graph.getSize() : sum[i - 1] + clique.get(i - 1));
			for (int j = 0; j < clique.get(i); j++)
			{
				Vertex v = creator.createVertex();
				graph.addVertex(v);
				result.add(v.getIndex());
			}

			for (int j = 0; j < clique.get(i); j++)
				for (int k = j + 1; k < clique.get(i); k++)
				{
					graph.addEdge(graph.getVertex(sum[i] + j), graph.getVertex(sum[i] + k));
					graph.addEdge(graph.getVertex(sum[i] + k), graph.getVertex(sum[i] + j));
				}
		}

		//		adding outer edges
		for (int i = 0; i < clique.size(); i++)
			for (int j = i + 1; j < clique.size(); j++)
				for (int k = 0; k < clique.get(i); k++)
					for (int r = 0; r < clique.get(j); r++)
					{
						if (RandomUtilities.nextDouble() <= d)
						{
							Vertex v1 = graph.getVertex(sum[i] + k);
							Vertex v2 = graph.getVertex(sum[j] + r);
							if (!v1.getOutEdges().contains(v2))
							{
								graph.addEdge(v1, v2);
								graph.addEdge(v2, v1);
							}
						}
					}

		return result;
	}

	private static void addEdges(Graph graph, List<Integer> ind1, List<Integer> ind2, double d)
	{
		for (int i = 0; i < ind1.size(); i++)
			for (int j = 0; j < ind2.size(); j++)
				if (RandomUtilities.nextDouble() <= d)
				{
					Vertex v1 = graph.getVertex(ind1.get(i));
					Vertex v2 = graph.getVertex(ind2.get(j));
					if (!v1.getOutEdges().contains(v2))
					{
						graph.addEdge(v1, v2);
						graph.addEdge(v2, v1);
					}
				}
	}
}
